package com.lingjtx.common.redis.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
import com.lingjtx.common.core.util.DateTimeUtil;
import com.lingjtx.common.thread.pooling.ObjectFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.text.SimpleDateFormat;
import java.util.Objects;

public class RedisTemplateFactory implements ObjectFactory<RedisTemplate<String, Object>> {
    private static final Logger log = LoggerFactory.getLogger(RedisTemplateFactory.class);

    private final RedisConnectionFactory connectionFactory;

    public RedisTemplateFactory(RedisConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
    }

    @Override
    public RedisTemplate<String, Object> create() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.setDateFormat(new SimpleDateFormat(DateTimeUtil.STANDARD_FORMAT));

        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
                .allowIfSubType("com.lingjtx") // 限制只允许自家包名下的类
                .allowIfSubType("java.util") // 👈 添加对 JDK 常用类的支持
                .build();
        objectMapper.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL);

        Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(objectMapper, Object.class);
        // 阿里 fastjson2 系列化
//        GenericFastJsonRedisSerializer fastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
//        redisTemplate.setDefaultSerializer(fastJsonRedisSerializer);//设置默认的Serialize，包含 keySerializer & valueSerializer
        // redis key 序列化设置 String
        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setValueSerializer(jsonRedisSerializer);
        // // redis HashKey 序列化设置 String
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        redisTemplate.setHashValueSerializer(jsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Override
    public boolean validate(RedisTemplate<String, Object> redisTemplate) {
        try {
            // 简单执行一下 ping 命令
            String pong = Objects.requireNonNull(redisTemplate.getConnectionFactory())
                    .getConnection()
                    .ping();
            return "PONG".equalsIgnoreCase(pong);
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public void destroy(RedisTemplate<String, Object> redisTemplate) {
        shutdown();
        try {
            // 关闭底层连接（RedisTemplate 本身没有 close 方法，关闭连接工厂的连接）
            if (redisTemplate.getConnectionFactory() != null) {
                redisTemplate.getConnectionFactory().getConnection().close();
            }
        } catch (Exception e) {
            // 打印异常，防止影响其他销毁流程
            log.error("redisTemplate 销毁时异常：{}", e.getMessage());
        }
    }

    @Override
    public void shutdown() {
        // shutdown 时，可以选择关闭 redisConnectionFactory
        if (connectionFactory != null) {
            try {
                connectionFactory.getConnection().close();
            } catch (Exception e) {
                // ignore
            }
        }
    }
}
